#include <stdint.h>                                                             
#include <stdio.h>                                                              
#include <stdlib.h>   
#include <string.h>
#include <fcntl.h>      
#include <unistd.h>
#include <getopt.h>
#include <sys/ioctl.h>                                                          
#include <linux/spi/spidev.h>   
#include <linux/types.h>

#define D_SPI_READ_OP			( 1 << 7 )

static const char *device = "/dev/spidev0.0";
static int s_fd = -1; 
static uint8_t mode = SPI_MODE_0;
static uint8_t bits = 8;  
static uint32_t speed = 24000000;  

static void pabort(const char *s)
{
	perror(s);
	abort();
}

void spi_init_test()
{
    int ret = 0;         
  
    s_fd = open(device, O_RDWR);  
    if (s_fd < 0)  
        pabort("can't open device");  

    printf("open device sucucess %s\n", device);  
   /* 
    * spi mode 
     */  
    ret = ioctl(s_fd, SPI_IOC_WR_MODE, &mode);  
    if (ret == -1)  
        pabort("can't set spi mode");  
  
    ret = ioctl(s_fd, SPI_IOC_RD_MODE, &mode);  
    if (ret == -1)  
        pabort("can't get spi mode");  
  
    /* 
     * bits per word 
     */  
    ret = ioctl(s_fd, SPI_IOC_WR_BITS_PER_WORD, &bits);  
    if (ret == -1)  
        pabort("can't set bits per word");  
  
    ret = ioctl(s_fd, SPI_IOC_RD_BITS_PER_WORD, &bits);  
    if (ret == -1)  
        pabort("can't get bits per word");  
  
    /* 
     * max speed hz 
     */  
    ret = ioctl(s_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);  
    if (ret == -1)  
        pabort("can't set max speed hz");  
  
    ret = ioctl(s_fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);  
    if (ret == -1)  
        pabort("can't get max speed hz");  
 
    printf("spi mode: %d\n", mode);  
    printf("bits per word: %d\n", bits);  
    printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);  
}

int SPI_Transfer(const uint8_t * tx_buf, uint8_t * rx_buf, int len)
{
	int ret;
	int i;

	struct spi_ioc_transfer tr ={
		.tx_buf = (unsigned long) tx_buf,
		.rx_buf = (unsigned long) rx_buf,
		.len =len,
		.delay_usecs = 0,
	};

	ret = ioctl(s_fd, SPI_IOC_MESSAGE(1), &tr);
	if (ret < 1)
		printf("can't send spi message");
	else
	{	
		printf("SPI Send [Len:%d]\n", len);
		printf("SPI Receive [len:%d]:", len);

		for (i = 0; i < len; i++)
		{
			printf("0x%02x ", rx_buf[i]);	// rx_buf[0]为0x00, rx_buf[1]是有效数据，还需找找原因。
		}
		printf("\n");
	}

	return ret;
}

int8_t user_spi_read(uint8_t reg_addr, uint8_t *data, uint16_t len)
{
	int retval = 0;
	int i = 0, index = 0;
	int tmp = 0;
	int fd = -1;
	char file_name[0x20];
	unsigned char buf[0x20] = {0};
	struct spi_ioc_transfer mesg[1];
	unsigned int spi_num = 0, csn = 0, cur_reg_addr; 
	unsigned int num_reg = 1, reg_width = 1, data_width = 1, reg_order = 1, data_order = 1; //dev_width = 1, 
    uint8_t tempValue[6];

	num_reg = len;
	sprintf(file_name, "/dev/spidev%u.%u", spi_num, csn);

	fd = open(file_name, 0);
	if (fd < 0)
	{
		printf("Open %s error!\n", file_name);
		retval = -1;
		goto end0;
	}

	printf("%s open!\n", file_name);

	tmp = SPI_MODE_0;
	retval = ioctl(fd, SPI_IOC_WR_MODE, &tmp);
	if (retval)
	{
		printf("set spi mode fail!\n");
		retval = -1;
		goto end1;
	}

	memset(mesg, 0, sizeof mesg);

	mesg[0].tx_buf = (__u32)buf;
	mesg[0].rx_buf = (__u32)buf;
	mesg[0].len = reg_width + data_width; 
	mesg[0].speed_hz = 24000000;  
	mesg[0].bits_per_word = 8;
	mesg[0].cs_change = 1;

	memset(buf, 0, sizeof buf);
	memset(tempValue, 0, sizeof tempValue); 

	for (cur_reg_addr = reg_addr, i = 0; cur_reg_addr < reg_addr + num_reg; cur_reg_addr++, i++)
	{
		index = 0;         

		if (reg_width == 1)
		{
			*(__u8 *)(&buf[index]) = (cur_reg_addr | D_SPI_READ_OP) & 0xff;
			index++;
		}
		else
		{
			if (reg_order)
				*(__u16 *)(&buf[index]) = cur_reg_addr & 0xffff;
			else
			{
				*(__u8 *)(&buf[index]) = (cur_reg_addr >> 8) & 0xff;
				*(__u8 *)(&buf[index + 1]) = cur_reg_addr & 0xff;
			}
			index += 2;
		}

		if (data_width == 1)
		{
			*(__u8 *)(&buf[index]) = 0x00;
		}
		else
		{
			*(__u16 *)(&buf[index]) = 0x0000;
		}

		retval = ioctl(fd, SPI_IOC_MESSAGE(1), mesg);
		if (retval != (int)mesg[0].len)
		{
			printf("SPI_IOC_MESSAGE error \n");
			retval = -1;
			goto end1;
		}

		retval = 0;
		if (data_width == 1)
		{
			tmp = *(__u8 *)(&buf[index]);
	
			tempValue[i]=tmp;			
			printf("index is %d, tmp is %d, i is %d, data is %u \n\n",index, tmp, i, *data); 
		}         
		else
		{
			if (data_order)
				tmp = *(__u16 *)(&buf[index]);
			else
			{
				tmp = *(__u8 *)(&buf[index]) << 8;
				tmp += (*(__u8 *)(&buf[index + 1]));
			}
		}
         
		 *data=tempValue[0]; 
	}

   //copy multi bytes to DATA array!!
	if (i>2){	   
		 for(int j=0;j<6;j++){			 
			*(data+j) = tempValue[j];
			printf("Test :data[%d] is  %d  ,tempValue[%d] is  %d \n", j,*(data+j) ,j,tempValue[j]);  //*data++ data[j]
		 }			
	 }
    
end1:
	close(fd);

end0:
	return retval;
}

int8_t user_spi_write(uint8_t reg_addr, uint8_t *data, uint16_t len)
{

	int retval = 0;
	int  index = 0;
	int tmp = 0;
	int fd = -1;
	char file_name[0x20];
	unsigned char buf[0x10] = {0};
	struct spi_ioc_transfer mesg[1];
	unsigned int spi_num = 0, csn = 0; 
	unsigned int  reg_width = 1, data_width = 1, reg_order = 1, data_order = 1;  //dev_width = 1,

	sprintf(file_name, "/dev/spidev%u.%u", spi_num, csn);

	fd = open(file_name, 0);
	if (fd < 0)
	{
		printf("Open %s error!\n", file_name);
		retval = -1;
		goto end0;
	}

	tmp = SPI_MODE_0;
	retval = ioctl(fd, SPI_IOC_WR_MODE, &tmp);
	if (retval)
	{
		printf("set spi mode fail!\n");
		retval = -1;
		goto end1;
	}

	memset(mesg, 0, sizeof mesg);

	mesg[0].tx_buf = (__u32)buf;
	mesg[0].rx_buf = (__u32)buf;
	mesg[0].len = reg_width + data_width; //dev_width +
	mesg[0].speed_hz = 24000000;
	mesg[0].bits_per_word = 8;
	mesg[0].cs_change = 1;
   usleep(2000); //add test 
	memset(buf, 0, sizeof buf);

	if (reg_width == 1)
	{
		*(__u8 *)(&buf[index]) = reg_addr & 0xff;
		index++;
	}
	else
	{
		if (reg_order)
			*(__u16 *)(&buf[index]) = reg_addr & 0xffff;
		else
		{
			*(__u8 *)(&buf[index]) = (reg_addr >> 8) & 0xff;
			*(__u8 *)(&buf[index + 1]) = reg_addr & 0xff;
		}
		index += 2;
	}

	if (data_width == 1)
		*(uint8_t *)(&buf[index]) = (*data) & 0xff;
	else
	{
		if (data_order)
			*(__u16 *)(&buf[index]) = (*data) & 0xffff;

		else
		{
			*(__u8 *)(&buf[index]) = (*data >> 8) & 0xff;
			*(__u8 *)(&buf[index + 1]) = (*data) & 0xff;
		}
	}

	// reverse8(buf, mesg[0].len);

	retval = ioctl(fd, SPI_IOC_MESSAGE(1), mesg);
	if (retval != (int)mesg[0].len)
	{
		printf("SPI_IOC_MESSAGE error \n");
		retval = -1;
		goto end1;
	}
	retval = 0;

end1:
	close(fd);

end0:
	return retval;
}

int8_t user_spi_readACC(uint8_t reg_addr, uint8_t *data, uint16_t len)
{
	int fd;
	int ret = -1;
	int tmp = 0;
	unsigned char buf[0x10] = {0};
	struct spi_ioc_transfer tr[1];
   
	fd = open("/dev/spidev0.0", O_RDWR);
	if (fd < 0)
	{
		printf("failed to open spidev0.0\n");
	}

	tmp = SPI_MODE_0; //  | SPI_LSB_FIRST;

	ret = ioctl(fd, SPI_IOC_WR_MODE, &tmp);
	if (ret)
	{
		printf("set spi mode fail!\n");
		ret = -1;
		goto end_spi_read;
	}
	
	memset(tr, 0, sizeof tr);
	tr[0].tx_buf = (__u32)buf;
	tr[0].rx_buf = (__u32)buf;
	tr[0].len = 2; 
	tr[0].speed_hz = 24000000;
	tr[0].bits_per_word = 8;
	tr[0].cs_change = 1;
    
	buf[0] = reg_addr;
	for (size_t i = 0; i < len; i++)
	{
		buf[0] = (reg_addr + i) | D_SPI_READ_OP;
		ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
		*(data+i) = buf[1];
		printf("Test Data %d ,buf[1] is 0x%x \n",*(data+i),buf[1]);
		//usleep(1000);
	}

    if (ret != tr[0].len) {
        printf("SPI_IOC_MESSAGE error \n");
        ret = -1;
        goto end_spi_read;
    }
   ret=0;

end_spi_read:
	close(fd);

	return ret;
}

int main()
{
	uint8_t r_buf[16] = {0};
	//uint8_t w_buf[12] = {0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a};
	uint8_t w_buf[1] = {0x75 | D_SPI_READ_OP};  // id
	uint8_t ag_buf[1] = {0x1f | D_SPI_READ_OP};
	uint8_t id = 0x0;
	int s = 0;

	spi_init_test();
	while (1) {
		//SPI_Transfer(w_buf, r_buf, 2);    
		SPI_Transfer(ag_buf, r_buf, 12);
		//user_spi_read(0x75, &id, 1);		
		//user_spi_read(0x1f, r_buf, 12);
		//user_spi_readACC(0x75, &id, 1);	
		//printf("id = %#x\n", id);
		//printf("%d\n", s++);
		usleep(1000*5);

	}

	return 0;
}
